﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

using XRpgLibrary.TileEngine;
using RpgLibrary.WorldClasses;
using RpgLibrary.SpriteClasses;

namespace XRpgLibrary.SpriteClasses
{
    public class AnimatedSprite
    {

        #region Field Region

        Dictionary<AnimationKey, Animation> animations;
        AnimationKey currentAnimation;
        bool isAnimating;
        Texture2D texture;
        public string TextureImageName;
        Vector2 position;
        Vector2 velocity;
        float speed = 200.0f;
        protected SpriteBatch spriteBatch;
        protected float opacity = 255;
        protected float rotation = 0;
        #endregion
        
        #region Property Region

        public float Rotation
        {
            get
            {
                return rotation;
            }
            set
            {
                rotation = value;
            }
        }

        private Vector2 RotationOrigin
        {
            get
            {
                return new Vector2(0,0);
            }
        }

        public float Opacity
        {
            get
            {
                return opacity;
            }
            set
            {
                opacity = MathHelper.Clamp(value, 0, 255);
            }
        }

        public Texture2D Texture
        {
            get { return texture; }
            set { texture = value; }
        }

        public AnimationKey CurrentAnimation
        {
            get { return currentAnimation; }
            set { currentAnimation = value; }
        }
        
        public bool IsAnimating
        {
            get { return isAnimating; }
            set { isAnimating = value; }
        }
        
        public int Width
        {
            get { return animations[currentAnimation].FrameWidth; }
        }
        
        public int Height
        {
            get { return animations[currentAnimation].FrameHeight; }
        }
        
        public float Speed
        {
            get { return speed; }
            set { speed = MathHelper.Clamp(value, 1.0f, 400.0f); }
        }

        public Vector2 Position
        {
            get { return position; }
            set
            {
                position = value;

            }
        }
        
        public Vector2 Velocity
        {
            get { return velocity; }
            set
            {
                velocity = value;
                if (velocity != Vector2.Zero)
                    velocity.Normalize();
            }
        }

        public Rectangle Bounds
        {
            get
            {
                return texture.Bounds;
            }
        }

        public SpriteBatch SpriteBatch
        {
            get
            {
                return spriteBatch;
            }
            set
            {
                spriteBatch = value;
            }
        }

        private Dictionary<AnimationKey, Animation> Animations
        {
            get
            {
                if (animations == null)
                    animations = new Dictionary<AnimationKey, Animation>();

                return animations;
            }
        }

        #endregion
        
        #region Constructor Region


        public AnimatedSprite(AnimatedSpriteData asData, ContentManager contentManager)
            : this(asData)
        {
            this.Texture = contentManager.Load<Texture2D>(this.TextureImageName);
        }
        

        public AnimatedSprite(AnimatedSpriteData asData)
        {
            this.currentAnimation = asData.CurrentAnimation;
            this.TextureImageName = asData.TextureImageName;
            this.isAnimating = asData.IsAnimating;
            this.Position = asData.Position;
            this.Velocity = asData.Velocity;
            this.Speed = asData.Speed;

            if (asData.Animations != null)
                foreach (AnimationKey key in asData.Animations.Keys)
                    this.Animations.Add(key, Animation.FromAnimationData(asData.Animations[key]));
        }

        public AnimatedSprite(Texture2D sprite, Dictionary<AnimationKey, Animation> animation, SpriteBatch spriteBatch)
        {
            texture = sprite;
            this.spriteBatch = spriteBatch;
            animations = new Dictionary<AnimationKey, Animation>();
            foreach (AnimationKey key in animation.Keys)
                animations.Add(key, (Animation)animation[key].Clone());
        }
        
        #endregion
        
        #region Method Region
        
        public void Update(GameTime gameTime)
        {
            if (isAnimating)
                animations[currentAnimation].Update(gameTime);
        }

        public void Draw(SpriteBatch renderTargetSpriteBatch)
        {
            renderTargetSpriteBatch.Draw(
                texture,
                position,
                animations[currentAnimation].CurrentFrameRect,
                new Color (255,255,255, (int)opacity), 
                rotation,
                RotationOrigin,
                1f,
                SpriteEffects.None,
                0f);
        }

        public void Draw(GameTime gameTime)
        {
            spriteBatch.Draw(
                texture,
                position,
                animations[currentAnimation].CurrentFrameRect,
                new Color(255, 255, 255, (int)opacity),
                rotation,
                RotationOrigin,
                1f,
                SpriteEffects.None,
                0f);
        }

        public void LockToMap()
        {
            position.X = MathHelper.Clamp(position.X, 0, TileMap.WidthInPixels - Width);
            position.Y = MathHelper.Clamp(position.Y, 0, TileMap.HeightInPixels - Height);
        }

        public void SetMapPosition(Vector2 position)
        {
            SetMapPosition((int)position.X, (int)position.Y);
        }

        public void SetMapPosition(int x, int y)
        {
            Position = new Vector2(
                RpgLibrary.RpgConstants.TileWidth * x,
                RpgLibrary.RpgConstants.TileHeight * y);
        }
        #endregion

        internal static AnimatedSprite FromAnimatedSpriteData(AnimatedSpriteData animatedSpriteData)
        {
            return new AnimatedSprite(animatedSpriteData);
        }

        public AnimatedSpriteData GetDataObject()
        {
            return new AnimatedSpriteData(
                GenerateAnimationData(),
                this.TextureImageName,
                this.Position,
                this.Velocity,
                this.Speed);
        }

        private Dictionary<AnimationKey, AnimationData> GenerateAnimationData()
        {
            Dictionary<AnimationKey, AnimationData> data = new Dictionary<AnimationKey, AnimationData>();
            foreach (AnimationKey key in animations.Keys)
                data.Add(key, animations[key].GetDataObject());

            return data;
        }
    }
}
